import { AmbientLight, Camera, Color, Light, MathUtils, PerspectiveCamera, Scene, Vector2, WebGLRenderer } from 'three';
import { ILoose, Utils } from '../tool/utils';
import { Observable } from './observable';
import { AbstractAssetTaskInfo } from '../loader/asset-task-info';
import { AssetsLoad } from '../loader/assets-load';
import { DefaultLoadingScreen } from '../loader/default-loading-screen';


/**
 * 舞台
 */
abstract class Stage {

    private _enabled: boolean = false;
    private _prepared: boolean = false;

    protected scenes!: Array<Scene>;
    protected camreas!: Array<Camera>;
    protected lights!: Array<Light>;

    public readonly engine: WebGLRenderer;
    public readonly canvas: HTMLCanvasElement;
    public readonly loader: AssetsLoad;
    public name: string = '';
    public id: string = MathUtils.generateUUID();
    public onUserGestureObservable: Observable<boolean> = new Observable();
    public onWindowResizeObservable: Observable<DOMRect> = new Observable();
    public onActiveObservable: Observable<boolean> = new Observable();
    public onPreparedObservable: Observable<void> = new Observable();
    public onReady: { (): void } | null = null;

    constructor(engine: WebGLRenderer, canvas: HTMLCanvasElement) {
        this.engine = engine;
        this.canvas = canvas;
        this.loader = new AssetsLoad();
        this.scenes = this.initializeScenes();
        this.camreas = this.initializeCameras(this.scenes);
        this.lights = this.initializeLights(this.scenes, this.camreas);
    }

    public get uiScene(): Scene {
        return this.scenes[0];
    }

    public get mainScene(): Scene {
        return this.scenes[0];
    }

    public get uiCamera(): Camera {
        return this.camreas[0];
    }

    public get mainCamera(): Camera {
        return this.camreas[0];
    }

    /**
     * 舞台初始化是否完成
     */
    public get prepared(): boolean {
        return this._prepared;
    }

    /**
     * 舞台是否启用
     */
    public get enable(): boolean {
        return this._enabled;
    }

    /**
     * 启用舞台
     */
    public set enable(enabled: boolean) {
        this._enabled = enabled;
        if (this._enabled) {
            if (!this._prepared) {
                this.loadAssets();
            } else {
                this.onEnable(false);
                this.onActiveObservable.notifyObservers(true);
            }

        } else {
            this.onDisable();
            this.onActiveObservable.notifyObservers(false);
        }
    }

    /**
     * 循环渲染
     */
    public render(): void {
        this.engine.render(this.mainScene, this.mainCamera);
    }

    /**
     * 重置场景
     */
    public reset(): void { }

    /**
     * 设置视角（视角重置、切换等）
     */
    public resetSight(): void { }

    /**
     * 窗体变化时执行
     * @param rect 矩形尺寸
     */
    public onWindowResize(rect: DOMRect): void { }

    /**
     * 用户行为
     * @param debut 首次
     */
    public onUserGesture(debut: boolean): void { }

    /**
     * 初始化场景
     * @returns 场景集合
     */
    protected initializeScenes(): Array<Scene> {
        const scene = new Scene();
        return [scene];
    }

    /**
     * 初始化相机
     * @param scenes 场景集合
     * @returns 相机集合
     */
    protected initializeCameras(scenes: Array<Scene>): Array<Camera> {
        const size = this.engine.getSize(new Vector2());
        const camera = new PerspectiveCamera(50, size.x / size.y, 0.1, 1000);
        return [camera];
    }

    /**
     * 初始化灯光
     * @param scenes 场景集合
     * @param cameras 相机集合
     * @returns 灯光集合
     */
    protected initializeLights(scenes: Array<Scene>, cameras: Array<Camera>): Array<Light> {
        const ambLight = new AmbientLight(new Color(0.5, 0.5, 0.5), 0);
        return [ambLight];
    }

    /**
     * 设置舞台资源信息
     * @returns 资源任务信息对象，或舞台资源信息集合
     */
    protected assetTaskInfos(): ILoose<AbstractAssetTaskInfo<any, any>> {
        return {};
    }

    /**
     * 加载资源
     */
    private loadAssets(): boolean {
        DefaultLoadingScreen.displayLoadingUI();
        const astTask = this.assetTaskInfos();
        const assets = Object.values(astTask);
        const initialize = async () => {
            this._prepared = true;
            this.onPreparedObservable.notifyObservers();
            await this.initializeComponents(this.scenes, this.camreas, this.lights);
            this.initializeInput(this.scenes, this.camreas);

            this.onEnable(true);
            this.onActiveObservable.notifyObservers(true);
            await Utils.sleep(0);
            DefaultLoadingScreen.hideLoadingUI();
            if (this.onReady) this.onReady();
        };
        if (assets.length == 0) {
            initialize();
            return true;
        }
        this.loader.load(assets,
            () => { },
            () => { },
            () => { },
            () => {
                initialize();
            },
            () => { }
        );

        return true;
    }

    /**
     * 舞台启用时执行
     * @param debut 是否首次启用
     */
    protected onEnable(debut: boolean): void { }

    /**
     * 舞台禁用时执行
     */
    protected onDisable(): void { }

    /**
     * 初始化业务组件
     * @param scenes 场景集合
     * @param cameras 相机集合
     * @param lights 灯光集合
     */
    protected async initializeComponents(scenes: Array<Scene>, cameras: Array<Camera>, lights: Array<Light>): Promise<void> { }

    /**
     * 初始化输入控制
     * @param scenes 场景集合
     * @param cameras 相机集合
     */
    protected initializeInput(scenes: Array<Scene>, cameras: Array<Camera>): void { }

}

/**
 * 舞台比较函数
 * @param a 舞台
 * @param b 舞台
 * @returns 是否相同
 */
const COMPARE_STAGE = (a: Stage, b: Stage) => {
    return a.id === b.id;
};

export { Stage, COMPARE_STAGE };